<template>
  <div class="player" v-show="playlist.length">
    <!-- transition通过fullScreen驱动过渡动画效果 -->
    <transition
      name="normal"
      @enter="enter"
      @afterEnter="afterEnter"
      @leave="leave"
      @afterLeave="afterLeave"
    >
      <div class="normal-player" v-show="fullScreen">
          <div class="background">
            <img :src="currentSong.pic">
          </div>
          <div class="top">
            <div class="back">
                <i class="icon-back" @click="goBack"></i>
            </div>
            <h1 class="title">{{currentSong.name}}</h1>
            <h2 class="subtitle">{{currentSong.singer}}</h2>
          </div>

          <!-- 中间唱片部分 start -->
          <div
            class="middle"
            @touchstart.prevent="onMiddleTouchStart"
            @touchmove.prevent="onMiddleTouchMove"
            @touchend.prevent="onMiddleTouchEnd"
          >
            <!-- CD start -->
            <div class="middle-l" :style="middleLStyle">
              <div class="cd-wrapper" ref="cdWrapperRef">
                <div
                  ref="cdRef"
                  class="cd"
                >
                  <img
                    ref="cdImage"
                    class="image"
                    :class="cdCls"
                    :src="currentSong.pic">
                </div>
              </div>
              <!-- 当前正在播放的歌词 -->
              <div class="playing-lyric-wrapper">
                <div class="playing-lyric">{{playingLyric}}</div>
              </div>
            </div>
            <!-- CD end -->
            <!-- 歌词部分 start -->
            <scroll
              class="middle-r"
              ref="lyricScrollRef"
              :style="middleRStyle"
            >
              <div class="lyric-wrapper">
                <!-- 有歌词 -->
                <div
                  v-if="currentLyric"
                  ref="lyricListRef"
                >
                  <p
                    class="text"
                    :class="{'current': currentLineNum === index}"
                    v-for="(line, index) in currentLyric.lines"
                    :key="index"
                  >
                    {{line.txt}}
                  </p>
                </div>
                <!-- 无歌词(纯音乐) -->
                <div class="pure-music" v-show="pureMusicLyric">
                  <p>{{pureMusicLyric}}</p>
                </div>
              </div>
            </scroll>
            <!-- 歌词部分 end -->
          </div>
          <!-- 中间唱片部分 end -->
          <div class="bottom">
            <!-- CD/歌词切换状态DOT start -->
            <div class="dot-wrapper">
              <span class="dot" :class="{'active': currentShow === 'cd'}"></span>
              <span class="dot" :class="{'active': currentShow === 'lyric'}"></span>
            </div>
            <!-- CD/歌词切换状态DOT end -->
            <!-- 进度条 start -->
            <div class="progress-wrapper">
              <!-- 当前歌曲播放进度 -->
              <span class="time time-l">{{formatTime(currentTime)}}</span>
              <div class="progress-bar-wrapper">
                <progressBar
                  ref="barRef"
                  :progress="progress"
                  @progress-changed="onProgressChanged"
                  @progress-changing="onProgressChanging"
                ></progressBar>
              </div>
              <!-- 当前歌曲总时长 -->
              <span class="time time-r">{{formatTime(currentSong.duration)}}</span>
            </div>
            <!-- 进度条 end -->
            <div class="operators">
              <div class="icon i-left">
                <i :class="modeIcon" @click="changeMode"></i>
              </div>
              <div class="icon i-left" :class="disabledCls">
                <i
                 :class="disabledCls"
                  class="icon-prev"
                  @click="prev"></i>
              </div>
              <div class="icon i-center">
                <i
                  :class="playIcon"
                  @click="togglePlay"></i>
              </div>
              <div class="icon i-right" :class="disabledCls">
                <i
                 :class="disabledCls"
                  class="icon-next"
                  @click="next"></i>
              </div>
              <div class="icon i-right">
                <i @click="toggleFavorite(currentSong)"
                  :class="getFavoriteIcon(currentSong)"
                ></i>
              </div>
            </div>
          </div>
      </div>
    </transition>

      <!-- 底部迷你播放器组件 start -->
      <mini-player :progress="progress" :togglePlay="togglePlay"></mini-player>
      <!-- 底部迷你播放器组件 end -->

      <audio
        ref="audioRef"
        @pause="pause"
        @canplay="ready"
        @error="error"
        @timeupdate="updateTime"
        @ended="end"
      ></audio>
  </div>
</template>

<script>
import { computed, watch, ref, nextTick } from 'vue';
import { useStore } from 'vuex';
import useMode from './use-mode.js';
import useFavorite from './use-favorite';
import useCD from './use-cd';
import useLyric from './use-lyric';
import progressBar from './progress-bar.vue';
import useMiddleInteractive from './use-middle-interactive';
import scroll from '@/components/base/scroll/scroll.vue';
import { formatTime } from '../../assets/js/util';
import { PLAY_MODE } from '@/assets/js/constant';
import MiniPlayer from './mini-player.vue';
import useAnimation from './use-animation';
import usePlayHistory from '../player/use-play-history';

export default {
  name: 'player',
  components: {
    MiniPlayer,
    progressBar,
    scroll
  },
  setup() {
    const audioRef = ref(null);
    // 歌曲缓冲标识(需要在歌曲缓冲完成之后才可以播放(audio空间canplay方法触发后))
    const songReady = ref(false);
    const store = useStore();
    const currentTime = ref(0); // 当前歌曲播放时长
    let progressChanging = false; // 进度条拖动标识
    const barRef = ref(null); // 进度条实例

    // 使用计算属性是为了响应式
    // vuex中的数据变化,计算属性就能随之变化,从而实现响应式
    const fullScreen = computed(() => store.state.fullScreen); // 播放器状态
    const currentSong = computed(() => store.getters.currentSong); // 当前播放歌曲数据
    // 当前播放歌曲索引
    const currentIndex = computed(() => store.state.currentIndex);
    // 获取当前播放模式
    const playMode = computed(() => {
      return store.state.playMode;
    });
    // 播放模式按钮
    const { modeIcon, changeMode } = useMode();
    const { getFavoriteIcon, toggleFavorite } = useFavorite();
    const { cdCls, cdImage, cdRef } = useCD();
    const {
      currentLyric,
      currentLineNum,
      playLyric,
      lyricScrollRef,
      lyricListRef,
      stopLyric,
      pureMusicLyric,
      playingLyric
    } = useLyric({ songReady, currentTime });
    const {
      currentShow,
      middleRStyle,
      middleLStyle,
      onMiddleTouchEnd,
      onMiddleTouchMove,
      onMiddleTouchStart
    } = useMiddleInteractive();

    const {
      cdWrapperRef,
      enter,
      afterEnter,
      leave,
      afterLeave
    } = useAnimation();

    // 最近播放
    const { savePlay } = usePlayHistory();

    // 当前播放列表
    const playlist = computed(() => store.state.playList);

    // 获取当前播放状态
    const playing = computed(() => store.state.playing);
    // 根据播放状态调整按钮图标
    const playIcon = computed(() => {
      return playing.value ? 'icon-pause' : 'icon-play';
    });
    // 切歌按钮禁用状态
    const disabledCls = computed(() => {
      return songReady.value ? '' : 'disable';
    });
    // 当前播放歌曲播放进度
    const progress = computed(() => {
      // 当前播放时长 / 总时长
      return currentTime.value / currentSong.value.duration;
    });

    // 当前播放歌曲发生变化时
    watch(currentSong, (newSong) => {
      if (!newSong.id || !newSong.url) {
        return;
      }
      // 切换歌曲需要将当前播放时长置为0
      currentTime.value = 0;
      // 当每次切换歌曲时将songReady改为false
      songReady.value = false;
      const audioEl = audioRef.value;
      audioEl.src = newSong.url;
      audioEl.play();
      store.commit('setPlayingState', true);
    });

    // 监听播放状态,关联audio播放控件
    watch(playing, (newPlaying) => {
      // 如果歌曲未缓冲完成则return
      if (!songReady.value) {
        return;
      }
      const audioEl = audioRef.value;
      // 歌曲当前播放状态发生变更时更新歌词播放状态
      if (newPlaying) {
        // 播放
        audioEl.play();
        playLyric();
      } else {
        // 暂停
        audioEl.pause();
        stopLyric();
      }
      // newPlaying ? audioEl.play() : audioEl.pause();
    });

    // 播放页面打开状态监听
    watch(fullScreen, async (newFullScreen) => {
      if (newFullScreen) {
        // 如果在Vue中直接访问DOM,如果是依赖一些数据变化,需要在nextTick之后再去访问DOM,才能正确的获取到想要的DOM,barRef中有对DOM的直接操作
        await nextTick();
        barRef.value.setOffset(progress.value);
      }
    });

    // 播放按钮(暂停/播放)
    function togglePlay() {
      if (!songReady.value) {
        return;
      }
      store.commit('setPlayingState', !playing.value);
    }
    // 音频文件播放暂停事件(用于处理非正常关闭,如笔记本合住盖子,非用户主动交互)
    function pause() {
      store.commit('setPlayingState', false);
    }

    // 后退
    function prev() {
      const list = playlist.value;
      let index = currentIndex.value - 1;
      // 如果列表中没有歌曲,则不作任何操作
      if (!list.length || !songReady.value) {
        return;
      }
      // 如果播放列表只有一首歌,则需要让这首歌曲循环播放
      if (list.length === 1) {
        loop();
      } else {
        // 边界处理,如果当前播放歌曲为第一首,则需要跳至列表最后一首歌
        if (index === -1) {
          index = list.length - 1;
        }
        store.commit('setCurrentIndex', index);
        // 如果在当前歌曲暂停状态下点击,则需要将状态改为播放
        if (!playing.value) {
          store.commit('setPlayingState', true);
        }
      }
    }

    // 前进
    function next() {
      const list = playlist.value;
      let index = currentIndex.value + 1;
      if (!list.length || !songReady.value) {
        return;
      }
      if (list.length === 1) {
        loop();
      } else {
        // 边界处理,如果当前播放歌曲为最后一首,则需要跳至列表第一首歌
        if (index === list.length) {
          index = 0;
        }
        store.commit('setCurrentIndex', index);
        // 如果在当前歌曲暂停状态下点击,则需要将状态改为播放
        if (!playing.value) {
          store.commit('setPlayingState', true);
        }
      }
    }

    // 循环播放当前歌曲
    function loop() {
      const audioEl = audioRef.value;
      // audioEl.currentTime将当前歌曲的播放位置(播放时间)置为0
      audioEl.currentTime = 0;
      audioEl.play();
      store.commit('setPlayingState', true);
    }

    // 歌曲缓冲完成后
    function ready() {
      // canplay函数会执行多次(歌曲会缓冲多次),只需要在第一次缓冲完成时触发
      if (songReady.value) {
        return;
      }
      songReady.value = true;
      // 播放歌词
      playLyric();
      savePlay(currentSong.value);
    }

    // 歌曲播放错误
    function error() {
      // 歌曲播放错误时,需要将songReady设置为true,否则将会无法切换歌曲,songReady会一直为false,允许用户在歌曲播放错误时进行切歌操作
      songReady.value = true;
    }

    // 当前歌曲播放位置发生变化时触发
    function updateTime(e) {
      // 实时更新当前播放时间
      // progressChanging为true时代表进度条正在被拖动,不更新当前播放时间
      // progressChanging为false时代表进度条拖动结束,可以更新当前播放时间
      if (!progressChanging) {
        currentTime.value = e.target.currentTime;
      }
    }

    // 拖动过程中手指还未离开
    function onProgressChanging(progress) {
      // 进度条拖动后来回跳问题
      // 进度条拖动至最新位置,但同时当前歌曲仍在播放,计算进度条的计算属性仍在执行,导致歌曲从最新位置由跳回当前播放位置,需要使用标识来判断
      progressChanging = true;
      // 计算左侧歌曲当前播放时长: 歌曲总时长 * 当前进度(百分比)
      // progress(进度条进度,相当于百分比)
      // currentSong.value.duration(歌曲总长度)
      // currentTime.value左侧播放时间
      currentTime.value = currentSong.value.duration * progress;
      // 先播放在停止是为了使歌词播放器同步到最新位置(根据已同步的当前播放时间计算得出)
      playLyric();
      // 正在拖动的过程中暂停歌词播放
      stopLyric();
    }

    // 拖动过程中手指离开
    function onProgressChanged(progress) {
      progressChanging = false;
      // 拖动过程中不会修改当前歌曲的播放时间,但是手指离开屏幕时,修改当前播放时间
      audioRef.value.currentTime = currentTime.value = currentSong.value.duration * progress;
      if (!playing.value) {
        store.commit('setPlayingState', true);
      }
      // 拖动结束后播放歌词
      playLyric();
    }

    // 歌曲播放结束
    function end() {
      // 将当前播放时间置为0
      currentTime.value = 0;
      // 结束时需要判断当前播放模式
      if (playMode.value === PLAY_MODE.loop) {
        // 如果当前是循环播放
        loop();
      } else {
        next();
      }
    }

    // 返回
    function goBack() {
      store.commit('setFullScreen', false);
    }

    return {
      goBack,
      audioRef,
      fullScreen,
      disabledCls,
      currentSong,
      playIcon,
      togglePlay,
      pause,
      next,
      prev,
      ready,
      error,
      changeMode,
      modeIcon,
      toggleFavorite,
      getFavoriteIcon,
      currentTime,
      progress,
      updateTime,
      formatTime,
      onProgressChanged,
      onProgressChanging,
      end,
      cdCls,
      cdImage,
      cdRef,
      currentLyric,
      currentLineNum,
      lyricListRef,
      lyricScrollRef,
      stopLyric,
      pureMusicLyric,
      playingLyric,
      currentShow,
      middleRStyle,
      middleLStyle,
      onMiddleTouchEnd,
      onMiddleTouchMove,
      onMiddleTouchStart,
      playlist,
      barRef,
      cdWrapperRef,
      afterEnter,
      enter,
      leave,
      afterLeave
    };
  }
};
</script>

<style lang="scss" scoped>
.player {
    .normal-player {
        position: fixed;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
        z-index: 150;
        background: $color-background;
        .background {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            z-index: -1;
            opacity: 0.6;
            filter: blur(20px);
            img {
                width: 100%;
                height: 100%;
            }
        }
        .top {
            position: relative;
            margin-bottom: 25px;
            .back {
                position: absolute;
                top: 0;
                left: 6px;
                z-index: 50;
            }
            .icon-back {
                display: block;
                padding: 9px;
                font-size: $font-size-large-x;
                color: $color-theme;
                transform: rotate(-90deg);
            }
            .title {
                width: 70%;
                margin: 0 auto;
                line-height: 40px;
                text-align: center;
                @include no-wrap();
                font-size: $font-size-large;
                color: $color-text;
            }
            .subtitle {
                line-height: 20px;
                text-align: center;
                font-size: $font-size-medium;
                color: $color-text;
            }
        }
        .middle {
          position: fixed;
          width: 100%;
          top: 80px;
          bottom: 170px;
          white-space: nowrap;
          font-size: 0;
          .middle-l {
            display: inline-block;
            // display: none;
            vertical-align: top;
            position: relative;
            width: 100%;
            height: 0;
            padding-top: 80%;
            .cd-wrapper {
              position: absolute;
              left: 10%;
              top: 0;
              width: 80%;
              box-sizing: border-box;
              height: 100%;
              .cd {
                width: 100%;
                height: 100%;
                border-radius: 50%;
                img {
                  position: absolute;
                  left: 0;
                  top: 0;
                  width: 100%;
                  height: 100%;
                  box-sizing: border-box;
                  border-radius: 50%;
                  border: 10px solid rgba(255, 255, 255, .1);
                }
                .playing {
                  animation: rotate 20s linear infinite;
                }
              }
            }
            .playing-lyric-wrapper {
            width: 80%;
            margin: 30px auto 0 auto;
            overflow: hidden;
            text-align: center;
            .playing-lyric {
              height: 20px;
              line-height: 20px;
              font-size: $font-size-medium;
              color: $color-text-l;
            }
          }
          }
          .middle-r {
          display: inline-block;
          vertical-align: top;
          width: 100%;
          height: 100%;
          overflow: hidden;
          .lyric-wrapper {
            width: 80%;
            margin: 0 auto;
            overflow: hidden;
            text-align: center;
            .text {
              line-height: 32px;
              color: $color-text-l;
              font-size: $font-size-medium;
              &.current {
                color: $color-text;
              }
            }
            .pure-music {
              padding-top: 50%;
              line-height: 32px;
              color: $color-text-l;
              font-size: $font-size-medium;
            }
          }
        }
        }
        .bottom {
          position: absolute;
          bottom: 50px;
          width: 100%;
          .dot-wrapper {
          text-align: center;
          font-size: 0;
          .dot {
              display: inline-block;
              vertical-align: middle;
              margin: 0 4px;
              width: 8px;
              height: 8px;
              border-radius: 50%;
              background: $color-text-l;
              &.active {
                width: 20px;
                border-radius: 5px;
                background: $color-text-ll;
              }
            }
          }
          .progress-wrapper {
            display: flex;
            align-items: center;
            width: 80%;
            margin: 0 auto;
            padding: 10px 0;
            .time {
              color: $color-text;
              font-size: $font-size-small;
              flex: 0 0 40px;
              line-height: 30px;
              width: 40px;
            }
            .time-l {
              text-align: left;
            }
            .time-r {
              text-align: right;
            }
            .progress-bar-wrapper {
              flex: 1;
            }
          }
          .dot-wrapper {

          }
          .operators {
            display: flex;
            align-items: center;
            .icon {
              flex: 1;
              color: $color-theme;
              &.disable {
                color: $color-theme-d;
              }
              i {
                font-size: 30px;
              }
            }
            .i-left {
              text-align: right;
            }
            .i-center {
              padding: 0 20px;
              text-align: center;
              i {
                font-size: 40px;
              }
            }
            .i-right {
              text-align: left;
            }
            .icon-favorite {
              color: $color-sub-theme;
            }
          }
        }
        &.normal-enter-active, &.normal-leave-active {
         transition: all .6s;
         .top, .bottom {
           transition: all .6s cubic-bezier(0.45, 0, 0.55, 1);
         }
        }
        &.normal-enter-from, &.normal-leave-to {
         opacity: 0;
         .top {
           transform: translate3d(0, -100px, 0);
         }
         .bottom {
           transform: translate3d(0, 100px, 0);
         }
        }
    }
}
</style>
